// Copyright (c) 2021, gottingen group.
// All rights reserved.
// Created by liyinbin lijippy@163.com

#include "abel/random/internal/fast_uniform_bits.h"

#include <random>

#include "gtest/gtest.h"

namespace abel {

    namespace random_internal {
        namespace {

            template<typename IntType>
            class fast_uniform_bitsTypedTest : public ::testing::Test {
            };

            using IntTypes = ::testing::Types<uint8_t, uint16_t, uint32_t, uint64_t>;

            TYPED_TEST_SUITE(fast_uniform_bitsTypedTest, IntTypes);

            TYPED_TEST(fast_uniform_bitsTypedTest, BasicTest) {
                using Limits = std::numeric_limits<TypeParam>;
                using FastBits = fast_uniform_bits<TypeParam>;

                EXPECT_EQ(0, FastBits::min());
                EXPECT_EQ(Limits::max(), FastBits::max());

                constexpr int kIters = 10000;
                std::random_device rd;
                std::mt19937 gen(rd());
                FastBits fast;
                for (int i = 0; i < kIters; i++) {
                    const auto v = fast(gen);
                    EXPECT_LE(v, FastBits::max());
                    EXPECT_GE(v, FastBits::min());
                }
            }

            template<typename UIntType, UIntType Lo, UIntType Hi, UIntType Val = Lo>
            struct FakeUrbg {
                using result_type = UIntType;

                static constexpr result_type (max)() { return Hi; }

                static constexpr result_type (min)() { return Lo; }

                result_type operator()() { return Val; }
            };

            using UrngOddbits = FakeUrbg<uint8_t, 1, 0xfe, 0x73>;
            using Urng4bits = FakeUrbg<uint8_t, 1, 0x10, 2>;
            using Urng31bits = FakeUrbg<uint32_t, 1, 0xfffffffe, 0x60070f03>;
            using Urng32bits = FakeUrbg<uint32_t, 0, 0xffffffff, 0x74010f01>;


            TEST(fast_uniform_bitsTest, RangeSize) {
                EXPECT_EQ((RangeSize<FakeUrbg<uint8_t, 0, 3>>()), 4);
                EXPECT_EQ((RangeSize<FakeUrbg<uint8_t, 2, 2>>()), 1);
                EXPECT_EQ((RangeSize<FakeUrbg<uint8_t, 2, 5>>()), 4);
                EXPECT_EQ((RangeSize<FakeUrbg<uint8_t, 2, 6>>()), 5);
                EXPECT_EQ((RangeSize<FakeUrbg<uint8_t, 2, 10>>()), 9);
                EXPECT_EQ(
                        (RangeSize<FakeUrbg<uint8_t, 0, std::numeric_limits<uint8_t>::max()>>()),
                        0);

                EXPECT_EQ((RangeSize<FakeUrbg<uint16_t, 0, 3>>()), 4);
                EXPECT_EQ((RangeSize<FakeUrbg<uint16_t, 2, 2>>()), 1);
                EXPECT_EQ((RangeSize<FakeUrbg<uint16_t, 2, 5>>()), 4);
                EXPECT_EQ((RangeSize<FakeUrbg<uint16_t, 2, 6>>()), 5);
                EXPECT_EQ((RangeSize<FakeUrbg<uint16_t, 1000, 1017>>()), 18);
                EXPECT_EQ((RangeSize<
                        FakeUrbg<uint16_t, 0, std::numeric_limits<uint16_t>::max()>>()),
                          0);

                EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 0, 3>>()), 4);
                EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 2, 2>>()), 1);
                EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 2, 5>>()), 4);
                EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 2, 6>>()), 5);
                EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 1000, 1017>>()), 18);
                EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 0, 0xffffffff>>()), 0);
                EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 1, 0xffffffff>>()), 0xffffffff);
                EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 1, 0xfffffffe>>()), 0xfffffffe);
                EXPECT_EQ((RangeSize<FakeUrbg<uint32_t, 2, 0xfffffffe>>()), 0xfffffffd);
                EXPECT_EQ((RangeSize<
                        FakeUrbg<uint32_t, 0, std::numeric_limits<uint32_t>::max()>>()),
                          0);

                EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 0, 3>>()), 4);
                EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 2, 2>>()), 1);
                EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 2, 5>>()), 4);
                EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 2, 6>>()), 5);
                EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 1000, 1017>>()), 18);
                EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 0, 0xffffffff>>()), 0x100000000ull);
                EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 1, 0xffffffff>>()), 0xffffffffull);
                EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 1, 0xfffffffe>>()), 0xfffffffeull);
                EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 2, 0xfffffffe>>()), 0xfffffffdull);
                EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 0, 0xffffffffffffffffull>>()), 0ull);
                EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 1, 0xffffffffffffffffull>>()),
                          0xffffffffffffffffull);
                EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 1, 0xfffffffffffffffeull>>()),
                          0xfffffffffffffffeull);
                EXPECT_EQ((RangeSize<FakeUrbg<uint64_t, 2, 0xfffffffffffffffeull>>()),
                          0xfffffffffffffffdull);
                EXPECT_EQ((RangeSize<
                        FakeUrbg<uint64_t, 0, std::numeric_limits<uint64_t>::max()>>()),
                          0);
            }

            TEST(fast_uniform_bitsTest, PowerOfTwoSubRangeSize) {
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint8_t, 0, 3>>()), 4);
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint8_t, 2, 2>>()), 1);
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint8_t, 2, 5>>()), 4);
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint8_t, 2, 6>>()), 4);
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint8_t, 2, 10>>()), 8);
                EXPECT_EQ((PowerOfTwoSubRangeSize<
                        FakeUrbg<uint8_t, 0, std::numeric_limits<uint8_t>::max()>>()),
                          0);

                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint16_t, 0, 3>>()), 4);
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint16_t, 2, 2>>()), 1);
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint16_t, 2, 5>>()), 4);
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint16_t, 2, 6>>()), 4);
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint16_t, 1000, 1017>>()), 16);
                EXPECT_EQ((PowerOfTwoSubRangeSize<
                        FakeUrbg<uint16_t, 0, std::numeric_limits<uint16_t>::max()>>()),
                          0);

                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 0, 3>>()), 4);
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 2, 2>>()), 1);
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 2, 5>>()), 4);
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 2, 6>>()), 4);
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 1000, 1017>>()), 16);
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 0, 0xffffffff>>()), 0);
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 1, 0xffffffff>>()),
                          0x80000000);
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint32_t, 1, 0xfffffffe>>()),
                          0x80000000);
                EXPECT_EQ((PowerOfTwoSubRangeSize<
                        FakeUrbg<uint32_t, 0, std::numeric_limits<uint32_t>::max()>>()),
                          0);

                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 0, 3>>()), 4);
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 2, 2>>()), 1);
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 2, 5>>()), 4);
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 2, 6>>()), 4);
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 1000, 1017>>()), 16);
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 0, 0xffffffff>>()),
                          0x100000000ull);
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 1, 0xffffffff>>()),
                          0x80000000ull);
                EXPECT_EQ((PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 1, 0xfffffffe>>()),
                          0x80000000ull);
                EXPECT_EQ(
                        (PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 0, 0xffffffffffffffffull>>()),
                        0);
                EXPECT_EQ(
                        (PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 1, 0xffffffffffffffffull>>()),
                        0x8000000000000000ull);
                EXPECT_EQ(
                        (PowerOfTwoSubRangeSize<FakeUrbg<uint64_t, 1, 0xfffffffffffffffeull>>()),
                        0x8000000000000000ull);
                EXPECT_EQ((PowerOfTwoSubRangeSize<
                        FakeUrbg<uint64_t, 0, std::numeric_limits<uint64_t>::max()>>()),
                          0);
            }

            TEST(fast_uniform_bitsTest, Urng4_VariousOutputs) {
                // Tests that how values are composed; the single-bit deltas should be spread
                // across each invocation.
                Urng4bits urng4;
                Urng31bits urng31;
                Urng32bits urng32;

                // 8-bit types
                {
                    fast_uniform_bits<uint8_t> fast8;
                    EXPECT_EQ(0x11, fast8(urng4));
                    EXPECT_EQ(0x2, fast8(urng31));
                    EXPECT_EQ(0x1, fast8(urng32));
                }

                // 16-bit types
                {
                    fast_uniform_bits<uint16_t> fast16;
                    EXPECT_EQ(0x1111, fast16(urng4));
                    EXPECT_EQ(0xf02, fast16(urng31));
                    EXPECT_EQ(0xf01, fast16(urng32));
                }

                // 32-bit types
                {
                    fast_uniform_bits<uint32_t> fast32;
                    EXPECT_EQ(0x11111111, fast32(urng4));
                    EXPECT_EQ(0x0f020f02, fast32(urng31));
                    EXPECT_EQ(0x74010f01, fast32(urng32));
                }

                // 64-bit types
                {
                    fast_uniform_bits<uint64_t> fast64;
                    EXPECT_EQ(0x1111111111111111, fast64(urng4));
                    EXPECT_EQ(0x387811c3c0870f02, fast64(urng31));
                    EXPECT_EQ(0x74010f0174010f01, fast64(urng32));
                }
            }

            TEST(fast_uniform_bitsTest, URBG32bitRegression) {
                // Validate with deterministic 32-bit std::minstd_rand
                // to ensure that operator() performs as expected.
                std::minstd_rand gen(1);
                fast_uniform_bits<uint64_t> fast64;

                EXPECT_EQ(0x05e47095f847c122ull, fast64(gen));
                EXPECT_EQ(0x8f82c1ba30b64d22ull, fast64(gen));
                EXPECT_EQ(0x3b971a3558155039ull, fast64(gen));
            }

        }  // namespace
    }  // namespace random_internal

}  // namespace abel
